package com.pfzy.utildemo.activity.record.sdk.pcm.aac;

import android.media.MediaCodec;
import android.media.MediaFormat;

import com.pfzy.lib.config.log.MyLog;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * AAC编码器
 */
public class AACEncoder {
    private static final String AUDIO_MIME_TYPE = "audio/mp4a-latm";
    private static Map<String, Integer> SAMPLE_RATE_TYPE = new HashMap();
    private MediaCodec aEncoder;
    private LinkedBlockingQueue<byte[]> audioQueue;
    private Callback mCallback;
    private int sampleRateType;

    public interface Callback {
        void outputAudioData(byte[] aac, int len, int nTimeStamp);// 输出音频数据
    }

    public void setCallback(Callback callback) {
        this.mCallback = callback;
    }

    public void start() {
        startAudioEncode();// 启动音频编码
    }

    public void stop() {
        stopAudioEncode();// 停止音频编码
    }

    /**
     * 启动音频编码
     */
    private void startAudioEncode() {
        this.aEncoder = initAudioEncoder();
        _logd("startAudioEncode");
        if (this.aEncoder == null) {
            _logd("aEncoder null");
            return;
        }
        this.aEncoder.setCallback(new MediaCodec.Callback() {
            /**
             * 输入缓冲区
             */
            @Override
            public void onInputBufferAvailable(MediaCodec codec, int index) {
                try {
                    ByteBuffer inputBuffer = codec.getInputBuffer(index);
                    inputBuffer.clear();
                    byte[] dataSources = audioQueue.poll();
                    int length = 0;
                    if (dataSources != null) {
                        inputBuffer.put(dataSources);
                        length = dataSources.length;
                    }
                    codec.queueInputBuffer(index, 0, length, 0L, 0);
                } catch (Exception e) {
                    _loge("onInputBufferAvailable Exception " + e);
                }
            }

            /**
             * 输出缓冲区
             */
            @Override
            public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
                ByteBuffer outputBuffer = codec.getOutputBuffer(index);
                if (outputBuffer != null && info.size > 0) {
                    if (info.size != 2) {
                        int outPacketSize = info.size + 7;
                        outputBuffer.position(info.offset);
                        outputBuffer.limit(info.offset + info.size);
                        byte[] chunkAudio = new byte[outPacketSize];
                        addADTStoPacket(chunkAudio, outPacketSize);
                        outputBuffer.get(chunkAudio, 7, info.size);
                        outputBuffer.position(info.offset);
                        if (mCallback != null) {
                            mCallback.outputAudioData(chunkAudio, chunkAudio.length, ((int) info.presentationTimeUs) / 1000);
                        }
                    } else {
                        _loge("skip audio decoder specific info");
                    }
                }
                codec.releaseOutputBuffer(index, false);
            }

            @Override
            public void onError(MediaCodec codec, MediaCodec.CodecException e) {
                _loge("onError " + e);
            }

            @Override
            public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
                _logd("onOutputFormatChanged format: " + format.toString());
            }
        });
        this.aEncoder.start();
    }

    /**
     * 初始化音频编码器
     */
    public MediaCodec initAudioEncoder() {
        this.sampleRateType = SAMPLE_RATE_TYPE.get(AACRecordHelper.getInstance().getSampleRate() + "");
        this.audioQueue = new LinkedBlockingQueue<>();
        if (this.aEncoder != null) {
            return this.aEncoder;
        }
        int sampleRate = AACRecordHelper.getInstance().getSampleRate();
        MediaFormat audioFormat = MediaFormat.createAudioFormat(AUDIO_MIME_TYPE, sampleRate, 1);
        audioFormat.setInteger("aac-profile", 2);
        audioFormat.setInteger("channel-mask", 16);
        audioFormat.setInteger("bitrate", 15600);
        audioFormat.setInteger("channel-count", 1);
        audioFormat.setInteger("sample-rate", sampleRate);
        audioFormat.setInteger("max-input-size", 4096);
        _logd("initAudioEncoder format: " + audioFormat.toString());
        MediaCodec mediaCodec = null;
        try {
            mediaCodec = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
        } catch (IOException e) {
            e.printStackTrace();
            _loge("初始化音频编码器失败 " + e);
        }
        if (mediaCodec == null) {
            return null;
        }
        mediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        return mediaCodec;
    }

    /**
     * 停止音频编码
     */
    private void stopAudioEncode() {
        _logd("stopAudioEncode");
        if (this.aEncoder != null) {
            this.aEncoder.stop();
            this.aEncoder.release();
            this.aEncoder = null;
        }
    }

    /**
     * 存入音频数据
     */
    public void putAudioData(byte[] data) {
        try {
            this.audioQueue.put(data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 将ADTS添加到数据包
     */
    public void addADTStoPacket(byte[] packet, int packetLen) {
        int freqIdx = this.sampleRateType;
        packet[0] = -1;
        packet[1] = -7;
        packet[2] = (byte) ((freqIdx << 2) + 64 + 0);
        packet[3] = (byte) ((packetLen >> 11) + 64);
        packet[4] = (byte) ((packetLen & 2047) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 31);
        packet[6] = -4;
    }

    static {
        SAMPLE_RATE_TYPE.put("96000", 0);
        SAMPLE_RATE_TYPE.put("88200", 1);
        SAMPLE_RATE_TYPE.put("64000", 2);
        SAMPLE_RATE_TYPE.put("48000", 3);
        SAMPLE_RATE_TYPE.put("44100", 4);
        SAMPLE_RATE_TYPE.put("32000", 5);
        SAMPLE_RATE_TYPE.put("24000", 6);
        SAMPLE_RATE_TYPE.put("22050", 7);
        SAMPLE_RATE_TYPE.put("16000", 8);
        SAMPLE_RATE_TYPE.put("12000", 9);
        SAMPLE_RATE_TYPE.put("11025", 10);
        SAMPLE_RATE_TYPE.put("8000", 11);
        SAMPLE_RATE_TYPE.put("7350", 12);
    }

    private void _logd(String msg) {
        MyLog.logd("AAC编码器 " + msg);
    }

    private void _loge(String msg) {
        MyLog.loge("AAC编码器 " + msg);
    }
}
